「注意,您的表單尚未填寫完成」
好,我知道了。 (按下確定)
欸? 為什麼關不掉!?
不是按下確定就要關掉了嗎?
這是誰做的爛東西啊,看我怎麼整死你!
(開發者人員工具打開後,兔兔驚呆。)
哦 ... 什麼嘛~原來是我之前的未完成品啊~ 其實也不是做得很爛啦! 只是那時候比較趕時間所以 ...
「兔兔,我們都聽到囉~」
齁,不是這樣的,就,呃 ... 好啦!
(咳咳!)
那看來今天的任務就是把它做完整,來雪恥了!!!
首先,在專案裡的 ./src/components
資料夾中新增一個 Modal.vue
的元件:
然後:
<template>
</template>
<script>
export default {
name: "Modal",
}
</script>
為了成品的美觀,修改一下 App.vue
內的樣式,接著把元件新增到畫面中:
<template>
<div :class="[
'w-screen h-screen',
'p-5'
]">
<Modal />
</div>
</template>
<script>
import Modal from './components/Modal.vue'
export default {
data() {
return {
}
},
components: {
Modal,
}
}
</script>
OK,下一步!
今天要參考的範本是 ...
有沒有覺得很面熟啦~
沒錯,就是我們的 Creator!
今天要做出來的成品和 Creator 的 Modal 本身沒有什麼差異哦~
首先,來分析一下要素:
主要類別就可以分成後面的覆蓋層
,和前面的視窗
兩類。 但視窗其實也還能再簡易的切一下:
這樣推理線索都具備後,就可以開始名偵探兔兔
的神還原了!
依據各個線索來推斷,可以得到下面的結論:
半透明
和滿版
!完全置中
於畫面,壓在覆蓋層之上,然後 ... 那完美弧度,應該是傳說中的小米定律
導致的
左、右
各有一個元素,看來是和手風琴選單相似
的犯罪手法「我應該沒有漏說的吧?因為 ... 真相永遠只有一個!」
當然 ... 因為前一次造下這犯行的人就是我自己啊 QQ
為了雪恥當然得更認真點!
來吧! 第一個先完成覆蓋層和視窗的基底:
<div class="fixed top-0 left-0 w-screen h-screen p-5 flex justify-center items-center">
<!-- Modal-Overlay -->
<div class="absolute top-0 left-0 w-full h-full bg-black/50" />
<!-- Modal-Window -->
<div class="w-full max-w-sm bg-white rounded-md overflow-hidden z-10">
這是 Modal 視窗
</div>
</div>
對了,有注意到上面覆蓋層的 bg-black/50
嗎? 這就是之前在 Day 16 說過的便捷語法,快速就可以上完顏色和不透明度了。
還有,因為覆蓋層的位置是 absolute
,所以會蓋住視窗,記得加上 z-10
來調整 z 軸位置,把視窗的顯示層級往上拉。
那上面都完成了,就可以準備來刻視窗內部了。
其實視窗的結構和 Day 27 的手風琴選單極為類似
,大部分東西都是一樣的,而視窗內容區塊的設計也與手風琴選單內容區的設計相同,移植
過來再修改一下
,就會像這樣子:
<!-- Modal-Window -->
<div class="w-full max-w-sm bg-white rounded-md overflow-hidden z-10">
<div class="border-b-2 p-3 flex justify-between items-center">
<div class="font-bold text-gray-700">
注意
</div>
<div class="h-7 w-7 p-1 hover:bg-gray-200 active:scale-90 rounded-md cursor-pointer transition-all">
<svg xmlns="http://www.w3.org/2000/svg" class="h-full w-full" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<div>
<slot name="itemContent">
<div class="p-3">
<slot name="itemText">
這是 Modal 視窗
</slot>
</div>
</slot>
</div>
</div>
順便把視窗標題的文字變成 Props
,這樣們就可以從外部修改了:
<!-- 應用時 -->
<Modal title="嗨">
<template v-slot:itemText>
你好
</template>
</Modal>
完美至極!
接下來就要來完成我們最需要雪恥的 Modal 視窗開關部份了!
為了還要可以再度開啟視窗,我們會用上 Day 26 做過的按鈕哦~
我們就,開始吧!
首先,先來完成關閉的功能。 能夠用來點擊後關閉視窗的不外乎就是覆蓋層和叉叉兩處,但為了儲存狀態
,我們在 data 中要增加一個變數:
export default {
name: "Modal",
props: {
title: {
default: "注意"
}
},
data() {
return {
showed: true,
}
},
}
要先設為 true
哦!
不然等等一開始 Modal 就消失了 XD
然後寫一個 methods 的函數 closing()
來處理關閉事件:
export default {
name: "Modal",
props: {
title: {
default: "注意"
}
},
data() {
return {
showed: true,
}
},
+ methods: {
+ closing() {
+ this.showed = false
+ }
+ }
}
然後把我們所寫的函數應用上 template 中的覆蓋層和叉叉,做為它們 onclick 事件
所觸發的動作:
<div class="fixed top-0 left-0 w-screen h-screen p-5 flex justify-center items-center">
<!-- Modal-Overlay -->
<div
class="absolute top-0 left-0 w-full h-full bg-black/50"
@click="closing()"
/>
<!-- Modal-Window -->
<div class="w-full max-w-sm bg-white rounded-md overflow-hidden z-10">
<div class="border-b-2 p-3 flex justify-between items-center">
<div class="font-bold text-gray-700">
{{ title }}
</div>
<div
class="h-7 w-7 p-1 hover:bg-gray-200 active:scale-90 rounded-md cursor-pointer transition-all"
@click="closing()"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-full w-full" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<div>
<slot name="itemContent">
<div class="p-3">
<slot name="itemText">
這是 Modal 視窗
</slot>
</div>
</slot>
</div>
</div>
</div>
但現在按了仍然還不會有效果,因為我們還沒有讓 Modal 有相對應的樣式變化。
不過因為兔兔的做法和外面一般的實現方法有些許不同,所以在這裡先介紹一個會用到的特殊 CSS 屬性: pointer-events
在 CSS 中,若想要讓點擊事件穿透到元素背後,使這個元素不會觸發點擊事件的話,pointer-events
就是你要找的功能。
在 Tailwind 中也有相對應的功能性 class,若要讓點擊穿透,就在該元素中使用 pointer-events-none
,若要恢復,則是 pointer-events-auto
。
所以在這裡,兔兔決定若是不顯示 Modal 時,Modal 整體會變成完全透明且可點擊穿透
,顯示 Modal 時則完全不透明且不可點擊穿透
。我們用三元來實現:
<div
:class="[
'fixed top-0 left-0',
'w-screen h-screen',
'p-5',
'flex justify-center items-center',
'transition-all duration-300',
showed? 'opacity-100': 'opacity-0 pointer-events-none'
]"
>
<!-- Modal-Overlay -->
<div
class="absolute top-0 left-0 w-full h-full bg-black/50"
@click="closing()"
/>
<!-- Modal-Window -->
...
做到這裡,去點擊 Modal 的叉叉或覆蓋層應該馬上就有效果了,Modal 會淡出。
不過這樣還不夠華麗,我們順便追加一下 Modal 視窗的動畫。
Modal 視窗的部份我們讓它在**不顯示 Modal **時,會縮放到 0
;相反的,顯示 Modal 時,縮放程度會回到 100
,也就是不縮放。這個我們也用三元運算子來做:
<!-- Modal-Window -->
<div
:class="[
'w-full max-w-sm bg-white rounded-md overflow-hidden z-10',
showed? 'scale-100': 'scale-0',
'transition-all duration-300'
]"
>
<div class="border-b-2 p-3 flex justify-between items-center">
<div class="font-bold text-gray-700">
{{ title }}
</div>
<div
class="h-7 w-7 p-1 hover:bg-gray-200 active:scale-90 rounded-md cursor-pointer transition-all"
@click="closing()"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-full w-full" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
...
哈! 這樣是不是很好看呢?
現在可以關起來了,可是卻打不開 ...
不過也不用擔心啦,把我們之前做的按鈕拿出來用,讓我們按下按鈕來打開 Modal 吧!
但是要可以從外部開啟,我們還必須在內部動一點手腳。
要可以從外部收取是否展開 Modal 的訊號,我們就要加上一個 Props 參數:
export default {
name: "Modal",
props: {
showModal: {},
title: {
default: "注意"
}
},
...
}
加好之後,別忘了做進度條時的事情,要監控外部
傳來的變數內容來更新內部
的變數,還有在元件掛載時
也要做一次:
export default {
name: "Modal",
props: {
showModal: {},
title: {
default: "注意"
}
},
data() {
return {
showed: true,
}
},
mounted() {
this.showed = this.showModal
},
watch: {
showModal(newVal, oldVal) {
this.showed = newVal
}
},
methods: {
closing() {
this.showed = false
}
}
}
雖然這樣看似完整了,不過還有一個小 bug。 我們在關閉事件 closing()
觸發時一直都是改變內部的變數
,但外部的狀態並不會被我們同步到
,所以我們必須用 emit
傳遞出去給父層:
methods: {
closing() {
this.showed = false
this.$emit("closing")
}
}
OK,這樣 Modal 元件應該是完成囉!
接著來測試吧~
我們用之前做過的按鈕快速來建立個測試環境。
我們必須現在 App.vue
中定義一個儲存 Modal 開啟狀態的變數,按鈕按下時會把變數變為 true,接收到來自 Modal 關閉事件觸發時,會把變數設為 false。
那實作起來大概就是這樣:
<template>
<div :class="[
'w-screen h-screen',
'p-5'
]">
<RabbitButton
class="w-40"
text="開啟 Modal"
color="yellow"
@click="showed=true"
/>
<Modal
title="嗨"
:showModal="showed"
@closing="showed=false"
>
<template v-slot:itemText>
兔兔你好
</template>
</Modal>
</div>
</template>
<script>
import RabbitButton from './components/RabbitButton.vue'
import Modal from './components/Modal.vue'
export default {
data() {
return {
showed: true,
}
},
components: {
RabbitButton,
Modal,
}
}
</script>
OK~ 這樣算是雪恥成功了吧?
還行嗎? 會不會太難呢?
其實你應該會發現寫久了,複雜度好像就那樣而已,更多的是樣式上的變化,還有怎麼做才會讓自己未來可以更加的方便,其實人家說好的程式,是要能一直往上擴充,而不是去反覆修改原有的內容。
希望大家都能做順利的自己造輪子嘿!(如果開輪胎廠了,記得要讓兔兔當股東。)
關於兔兔們:
( # 兔兔小聲說 )
明天就是最後一天了呢~
說實在一路走來心裡是很掙扎的,
因為根本就沒想過可以堅持到這裡,
而且,我這才發現自己在文章內容上的坑越挖越大了 XDDD加油!